From 952df23ea6a65d9629befc8a7374352c830de0ef Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Tue, 7 May 2024 14:49:30 -0500 Subject: [PATCH] return location for redirect --- .../usr/local/nginx/conf/auth_location.conf | 11 +++++-- .../usr/local/nginx/conf/auth_request.conf | 4 +-- .../rootfs/usr/local/nginx/conf/nginx.conf | 3 +- frigate/api/auth.py | 31 ++++++++++++------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/docker/main/rootfs/usr/local/nginx/conf/auth_location.conf b/docker/main/rootfs/usr/local/nginx/conf/auth_location.conf index 6f9235486..f6e291de1 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/auth_location.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/auth_location.conf @@ -7,12 +7,19 @@ location /auth { proxy_pass $upstream_auth; ## Headers - ## The headers starting with X-* are required. + + # First strip out all the request headers + # Note: This is important to ensure that upgrade requests for secure + # websockets dont cause the backend to fail + proxy_pass_request_headers off; + # Pass info about the request proxy_set_header X-Original-Method $request_method; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Content-Length ""; - proxy_set_header Connection ""; + # Pass along auth related info + proxy_set_header Authorization $http_authorization; + proxy_set_header Cookie $http_cookie; proxy_set_header X-CSRF-TOKEN "1"; ## Basic Proxy Configuration diff --git a/docker/main/rootfs/usr/local/nginx/conf/auth_request.conf b/docker/main/rootfs/usr/local/nginx/conf/auth_request.conf index 8e72d0c68..b054a6b97 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/auth_request.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/auth_request.conf @@ -17,6 +17,6 @@ proxy_set_header Remote-Name $name; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; -## Redirect to the redirection url in the location header +## Pass the location header back up if it exists auth_request_set $redirection_url $upstream_http_location; -error_page 401 =302 $redirection_url; +add_header Location $redirection_url; diff --git a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf index bb94f0112..e1289c458 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf @@ -268,7 +268,8 @@ http { } location /api/version { - include auth_request.conf; + # dont auth the healthcheck endpoint + auth_request off; access_log off; rewrite ^/api(/.*)$ $1 break; proxy_pass http://frigate_api; diff --git a/frigate/api/auth.py b/frigate/api/auth.py index c46b3a09a..5513e3ef7 100644 --- a/frigate/api/auth.py +++ b/frigate/api/auth.py @@ -115,13 +115,20 @@ def create_encoded_jwt(user, expiration, secret): def set_jwt_cookie(response, cookie_name, encoded_jwt, expiration): # TODO: ideally this would set secure as well, but that requires TLS - response.set_cookie(cookie_name, encoded_jwt, httponly=True, expires=expiration) + response.set_cookie( + cookie_name, encoded_jwt, httponly=True, expires=expiration, secure=False + ) @AuthBp.route("/auth") def auth(): + success_response = make_response({}, 202) + + fail_response = make_response({}, 401) + fail_response.headers["location"] = "/login" + if not current_app.frigate_config.auth.enabled: - return make_response({}, 202) + return success_response JWT_COOKIE_NAME = current_app.frigate_config.auth.cookie_name JWT_REFRESH = current_app.frigate_config.auth.refresh_time @@ -142,18 +149,16 @@ def auth(): if encoded_token is None: logger.debug("No jwt token found") - return make_response({}, 401) + return fail_response try: - response = make_response({}, 202) - token = jwt.decode(encoded_token, current_app.jwt_token) if "sub" not in token.claims: logger.debug("user not set in jwt token") - return make_response({}, 401) + return fail_response if "exp" not in token.claims: logger.debug("exp not set in jwt token") - return make_response({}, 401) + return fail_response user = token.claims.get("sub") current_time = int(time.time()) @@ -171,7 +176,7 @@ def auth(): ) if expiration <= current_time: logger.debug("jwt token expired") - return make_response({}, 401) + return fail_response # if the jwt cookie is expiring soon elif jwt_source == "cookie" and expiration - JWT_REFRESH <= current_time: @@ -180,13 +185,15 @@ def auth(): new_encoded_jwt = create_encoded_jwt( user, new_expiration, current_app.jwt_token ) - set_jwt_cookie(response, JWT_COOKIE_NAME, new_encoded_jwt, new_expiration) + set_jwt_cookie( + success_response, JWT_COOKIE_NAME, new_encoded_jwt, new_expiration + ) - response.headers["remote-user"] = user - return response + success_response.headers["remote-user"] = user + return success_response except Exception as e: logger.error(f"Error parsing jwt: {e}") - return make_response({}, 401) + return fail_response @AuthBp.route("/login", methods=["POST"])