From 640bbea802c9a730c8e88dbedf43efd15404af06 Mon Sep 17 00:00:00 2001 From: Rene Luria Date: Mon, 6 Jul 2026 14:14:02 +0200 Subject: [PATCH] fix: specific redirect codes for mobile --- myice/webapi.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/myice/webapi.py b/myice/webapi.py index 2004f1e..3181ebf 100644 --- a/myice/webapi.py +++ b/myice/webapi.py @@ -7,7 +7,7 @@ from fastapi import FastAPI, Header, HTTPException, Request, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, RedirectResponse from pydantic import BaseModel -from typing import Annotated +from typing import Annotated, Optional from . import myice # OIDC Configuration @@ -151,11 +151,10 @@ async def favico(): @app.get("/login") -async def login(request: Request): +async def login(request: Request, redirect_uri: Optional[str] = None): """Redirect to Infomaniak OIDC login""" - import urllib.parse - import secrets + import urllib.parse state = secrets.token_urlsafe(32) nonce = secrets.token_urlsafe(32) @@ -196,6 +195,16 @@ async def login(request: Request): samesite="Lax", max_age=300, ) + # Store target redirect URI (e.g. myice://callback) for mobile app flow + if redirect_uri: + response.set_cookie( + "oidc_target_redirect", + redirect_uri, + httponly=True, + secure=True, + samesite="Lax", + max_age=300, + ) return response @@ -204,12 +213,17 @@ async def callback(request: Request, code: str, state: str): """Handle OIDC callback and exchange code for token""" expected_state = request.cookies.get("oidc_state") code_verifier = request.cookies.get("oidc_verifier") + # Determine final redirect target (mobile app or web frontend) + target_redirect = request.cookies.get("oidc_target_redirect") or os.environ.get( + "FRONTEND_URL", "/" + ) # We should also delete state/nonce cookies once read if not expected_state or state != expected_state: - response = RedirectResponse("/?error=Invalid state parameter") + response = RedirectResponse(f"/{target_redirect}?error=Invalid state parameter") response.delete_cookie("oidc_state") response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_verifier") + response.delete_cookie("oidc_target_redirect") return response # Exchange authorization code for access token token_data = { @@ -227,14 +241,14 @@ async def callback(request: Request, code: str, state: str): # Log the error for debugging logging.error("Token exchange failed: %s", token_response) # Redirect back to frontend with error - frontend_url = os.environ.get("FRONTEND_URL", "/") error_detail = token_response.get( "error_description", "Failed to obtain access token" ) - error_response = RedirectResponse(f"{frontend_url}?error={error_detail}") + error_response = RedirectResponse(f"{target_redirect}?error={error_detail}") error_response.delete_cookie("oidc_state") error_response.delete_cookie("oidc_nonce") error_response.delete_cookie("oidc_verifier") + error_response.delete_cookie("oidc_target_redirect") return error_response access_token = token_response["access_token"] @@ -250,19 +264,19 @@ async def callback(request: Request, code: str, state: str): user_email = userinfo.get("email") if ALLOWED_USERS and user_email not in ALLOWED_USERS: # Redirect back to frontend with error - frontend_url = os.environ.get("FRONTEND_URL", "/") - response = RedirectResponse(f"{frontend_url}?error=User not authorized") + response = RedirectResponse(f"{target_redirect}?error=User not authorized") response.delete_cookie("oidc_state") response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_verifier") + response.delete_cookie("oidc_target_redirect") return response # Redirect back to frontend with access token and user info - frontend_url = os.environ.get("FRONTEND_URL", "/") - response = RedirectResponse(f"{frontend_url}#access_token={access_token}") + response = RedirectResponse(f"{target_redirect}#access_token={access_token}") response.delete_cookie("oidc_state") response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_verifier") + response.delete_cookie("oidc_target_redirect") return response